/*******************************************************************************
 * Copyright 2011 Towee.net
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/

package net.towee.server.persistence;

import java.util.List;
import java.util.Map;

import net.towee.model.account.AccountData;
import net.towee.model.account.AccountGroup;
import net.towee.model.account.AccountIdentity;
import net.towee.model.account.AccountProfile;
import net.towee.model.account.Board;
import net.towee.model.account.ContactsGroup;
import net.towee.model.account.Gadget;
import net.towee.model.account.LoginzaAccount;
import net.towee.model.account.Role;
import net.towee.model.group.GroupIdentity;
import net.towee.model.utils.Pair;
import net.towee.shared.exceptions.PersistenceException;

/**
 * Interface for the storage and retrieval of {@link AccountData}s.
 * 
 * @author c58
 */
public interface AccountStore {
	/**
	 * Initialize the account store. Implementations are expected to validate
	 * any configuration values, validate the state of the store, and perform an
	 * start-up action needed (e.g. load list of accounts into memory, establish
	 * connection to database, etc...).
	 * 
	 * @throws PersistenceException
	 */
	void initializeAccountStore() throws PersistenceException;
	
	/**
	 * This method create new account with unique mail address and nickName.
	 * If account already exists with this email and auth was by Loginza, 
	 * it merge accounts (attach new Loginza account to existing)
	 * 
	 * If account with required nickname exists it throw an exception
	 *  
	 * @param account
	 * @return TODO
	 * @throws PersistenceException 
	 */
	AccountData createAccount(AccountData account);

	/**
	 * Returns an {@link AccountData} for the given ID or null if not exists.
	 * 
	 * @param id
	 *            participant id of the requested account.
	 * @throws PersistenceException
	 */
	AccountData getAccountById(String id) throws PersistenceException;
	
	/**
	 * Account data with fields
	 * @param id
	 * @param fields
	 * @return
	 * @throws PersistenceException
	 */
	AccountData getAccountById(String id, String... fields) throws PersistenceException;

	/*
	 * LOGINZA account container
	 */
	/**
	 * Returns an {@link AccountData} for the given LoginzaAccount or null if
	 * not exists.
	 * 
	 * @param id
	 *            participant LoginzaAccount of the requested account.
	 */
	AccountData getAccountByLoginza(LoginzaAccount logacc) throws PersistenceException;

	/**
	 * Return an {@link AccountData} for given Email or null if not exists.
	 * 
	 * @param email
	 * @param fields TODO
	 * @return
	 */
	AccountData getAccountByEmail(String email, String... fields) throws PersistenceException;

	/**
	 * Attach new loginza account to user with given ID and return true if
	 * success. False if user not found or account exists.
	 * 
	 * Implementation must check for existing account with given LoginzaAccount.
	 * If account exists, this method merge two account. Result account based on current
	 * logged in account.
	 * 
	 * @param id
	 * @param logacc
	 * @return true if success, false if user not found or account exists.
	 */
	boolean attachLoginzaAccount(String id, LoginzaAccount logacc);

	/**
	 * Detach loginza account from user data with given ID.
	 * 
	 * @param id
	 * @param logacc
	 * @return true on success and false if user with provided ID not found or
	 *         detaching account not exists in user data.
	 */
	boolean detachLoginzaAccount(String id, LoginzaAccount logacc);

	/**
	 * Set online state of user with given user ID
	 * 
	 * @param state
	 * @return TODO
	 */
	boolean setOnlineState(String userId, boolean state);

	/**
	 * Update account identity.
	 * 
	 * If given identity have nickname, implementation check account with given
	 * id. If account already have some nickname, it don't update it. Otherwise
	 * it try update. If user with given nickname already exists, method return false; 
	 * @param userId TODO
	 * @param identity
	 * 
	 * @return TODO
	 * @throws PersistenceException 
	 */
	boolean updateAccountIdentity(String userId, AccountIdentity identity) throws PersistenceException;

	/**
	 * Update some setting with given key
	 * 
	 * @param userId
	 * @param key
	 * @param value
	 * @return TODO
	 */
	boolean updateSetting(String userId, String key, String value);

	/**
	 * Update settings container
	 * 
	 * @param userId
	 * @param settings
	 * @return TODO
	 */
	boolean updateSettings(String userId, Map<String, String> settings);

	/**
	 * Return profile of some account with given profile user ID. Profile data
	 * filtred by some PrivacyRule in profile. If data is not visible or user
	 * can't view profile it return null.
	 * 
	 * @param userId
	 * @return
	 * @throws PersistenceException TODO
	 */
	AccountProfile getProfile(String userId, String profileUserId) throws PersistenceException;

	/**
	 * Update account profile with given user Id
	 * 
	 * @param userId
	 * @param profile
	 * @return TODO
	 * @throws PersistenceException 
	 */
	boolean updateProfile(String userId, AccountProfile profile) throws PersistenceException;

	/**
	 * Add new board to user data
	 * 
	 * @param userId
	 * @param board
	 */
	void addBoard(String userId, Board board);

	/**
	 * Remove board with some index in user data with given id
	 * 
	 * @param userId
	 * @param id
	 * @return TODO
	 * @throws PersistenceException 
	 */
	boolean removeBoard(String userId, String id) throws PersistenceException;
	
	/**
	 * Rename some board of user with given id
	 * 
	 * @param userId
	 * @param id
	 * @param newName
	 * @return TODO
	 * @throws PersistenceException 
	 */
	boolean renameBoard(String userId, String id, String newName) throws PersistenceException;

	/**
	 * Attach new group to board of user with given id
	 * 
	 * @param userId
	 * @param boardId
	 * @param groupId
	 * @return TODO
	 */
	boolean attachGroupToBoard(String userId, String boardId, String groupId);

	/**
	 * Detach new group to board of user with given id
	 * 
	 * @param userId
	 * @param boardId
	 * @param groupId
	 * @return TODO
	 */
	boolean detachGroupFromBoard(String userId, String boardId, String groupId);

	/**
	 * Save gadget with some index in some board of user with given id
	 * 
	 * @param userId
	 * @param boardId
	 * @param gadget
	 * @return TODO
	 * @throws PersistenceException 
	 */
	boolean saveGadget(String userId, String boardId, Gadget gadget) throws PersistenceException;

	/**
	 * Add new gadget to board
	 * 
	 * @param userId
	 * @param boardId
	 * @param widget
	 * @return TODO
	 */
	boolean addGadgetToBoard(String userId, String boardId, Gadget widget);

	/**
	 * Remove some gadget from board of user with given id
	 * 
	 * @param userId
	 * @param boardId
	 * @param widget
	 * @return 
	 */
	boolean removeGadgetFromBoard(String userId, String boardId, String gadgetName);

	/**
	 * Add new group of contacts
	 * 
	 * @param userId
	 * @param group
	 */
	boolean addContactsGroup(String userId, ContactsGroup group);
	
	/**
	 * @param cgroupId
	 * @return group of contacts with given id of given user
	 * @throws PersistenceException 
	 */
	ContactsGroup getContactsGroup(String cgroupId) throws PersistenceException;

	/**
	 * Delete group of contacts
	 * @param userId TODO
	 * @param index
	 * @return TODO
	 */
	boolean removeContactsGroup(String userId, String groupId);

	/**
	 * Rename existing group of contacts
	 * @param newName
	 * @param index
	 * 
	 * @return TODO
	 */
	boolean renameContactsGroup(String groupId, String newName);

	/**
	 * Add new contact to some group
	 * @param account
	 * @param index
	 * 
	 * @return TODO
	 */
	boolean addContactToGroup(String groupId, AccountIdentity account);

	/**
	 * Delete some contact of group
	 * @param groupId
	 * @param account
	 * 
	 * @return TODO
	 * @throws PersistenceException 
	 */
	boolean removeContactFromGroup(String groupId, String... contactIds) throws PersistenceException;

	/*
	 * GROUPS
	 */
	/**
	 * Check if user is in some groups with given ids
	 * 
	 * @param id
	 *            User account id
	 * @return true if user entered to each group
	 */
	boolean isInGroups(String id, String... groups);

	/**
	 * Enter user with given ID to group. Implementation check test answer and
	 * enter user to group with default role.
	 * 
	 * @param userId
	 * @param groupId
	 * @param answer
	 * @return true if user successfully entered to group, false if test answer
	 *         is wrong.
	 * @throws PersistenceException 
	 */
	AccountGroup enterToGroup(String userId, String groupId, String answer) throws PersistenceException;
	
	/**
	 * Enter user with given ID to group with given role.
	 * 
	 * @param userId
	 * @param groupId
	 * @param role
	 * @return true if user successfully entered to group
	 * @throws PersistenceException 
	 */
	AccountGroup enterToGroup(String userId, GroupIdentity groupId, String answer, Role role) throws PersistenceException;

	/**
	 * Delete user with given ID from some group. It also detach given group
	 * from all board of given board.
	 * 
	 * @param userId
	 * @param groupId
	 * @return true if user successfully deleted from group
	 * @throws PersistenceException 
	 */
	boolean removeFromGroup(String userId, String groupId) throws PersistenceException;

	/**
	 * Return list of online users who in contacts of user with given id
	 * @param userId
	 * @return
	 * @throws PersistenceException 
	 */
	List<AccountIdentity> whosOnline(String userId) throws PersistenceException;
	
	/**
	 * Return list of users who is online in group with given Id. Implementation
	 * get only users who in contacts or in favorites of some user with given
	 * id. For getting full list by pages use extended method.
	 * 
	 * @param userId
	 * @param groupId
	 * @return
	 * @throws PersistenceException 
	 */
	List<AccountIdentity> whosOnline(String userId, String groupId) throws PersistenceException;

	/**
	 * Return list of users who is online in some group.
	 * 
	 * @param groupId
	 * @param start
	 * @param perpage
	 * @return
	 */
	List<AccountIdentity> whosOnline(String groupId, int start, int perpage);

	/**
	 * List of users in some group
	 * 
	 * @param groupId
	 * @param start
	 * @param perpage
	 * @return
	 */
	List<Pair<AccountIdentity, AccountGroup>> getUsersByGroup(String groupId, int start, int perpage);

	/**
	 * Find users by findStr. This method search ONLY in account identity data.
	 * If groupId is null it search global. If start and perpage is null it 
	 * return all found users. If onlineState is null it ignore online state
	 * of users.
	 * 
	 * @param findStr
	 * @param groupId
	 * @param onlineState
	 * @return
	 */
	List<AccountIdentity> findUsers(String findStr, String groupId,
			Boolean onlineState, int start, int perpage);
	
	/**
	 * It's extended search method. This method search in account identity
	 * by findStr and in profile by ProfileContainer. Profile searching filtred
	 * by PrivacyRules in each profile.
	 * @param profile
	 * @param groupId
	 * @param start
	 * @param perpage
	 * 
	 * @return
	 */
	List<AccountProfile> findUsersByProfile(AccountProfile profile, String groupId,
			int start, int perpage);
	
	/**
	 * Update password of user with given Id. Return true if update successful
	 * @param oldPassword
	 * @param newPassword
	 * @return
	 * @throws PersistenceException 
	 */
	boolean updatePassword(String userId, String oldPassword, String newPassword) throws PersistenceException;

	/**
	 * Delete account with given id
	 * @param testAccountId
	 * @throws PersistenceException 
	 */
	void removeAccount(String id) throws PersistenceException;
	
	/**
	 * Set lastSessionDate field in account data to current new Date(); 
	 * for given user id 
	 * @param userId
	 * @return TODO
	 */
	boolean updateLastSessionDate(String userId);

	/**
	 * @param string
	 * @return account identity with given id
	 * @throws PersistenceException 
	 */
	AccountIdentity getAccountIdentity(String string) throws PersistenceException;
	
	/**
	 * @param userId
	 * @param groupId
	 * @return true if user with given id is owner of contacts group with given id
	 */
	boolean contactsOwnedBy(String userId, String groupId);

	/**
	 * Attach new email to account
	 * @param userId
	 * @param email
	 * @return
	 */
	boolean attachEmail(String userId, String email);
	
	/**
	 * Detach email from account
	 * @param userId
	 * @param email
	 * @return
	 */
	boolean detachEmail(String userId, String email);

	/**
	 * @param nickName
	 * @return true if account with given nickname exists
	 */
	boolean checkAccountWithNickName(String nickName);
	
	/**
	 * Get account with given nickname. Return account data with given nickname
	 * or throw PersistenceException if not exists
	 * 
	 * @param nickName
	 * @param fields
	 * @return
	 */
	AccountData getAccountByNickname(String nickName, String... fields) throws PersistenceException;
	
	/**
	 * Apply role to user in some group
	 * 
	 * @param roleId
	 * @param userId
	 * @return TODO
	 * @throws PersistenceException
	 */
	boolean applyRoleToUser(String roleId, String userId, String groupId) throws PersistenceException;

	/**
	 * Update board
	 * @param userId TODO
	 * @param currentBoard
	 * @throws PersistenceException 
	 */
	void updateBoard(String userId, Board currentBoard) throws PersistenceException;

	/**
	 * This method try to find user with given cookie remember ID. On
	 * success it return user ID.
	 * @param coockieRememberId
	 * @return
	 */
	String getAccountByCookie(String coockieRememberId);
	
	/**
	 * Update cookie ID of user
	 * @param userId
	 * @param coockieRememberId
	 */
	void updateAccountCookie(String userId, String coockieRememberId);
}